#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Windows服务包装器
让任务调度器作为Windows服务运行
"""

import sys
import os
import logging
import time
import win32serviceutil
import win32service
import win32event
import win32api
import servicemanager
from typing import Optional

# 添加项目根目录到Python路径，以便正确导入模块
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
if parent_dir not in sys.path:
    sys.path.insert(0, parent_dir)

# 导入模块，支持相对导入和绝对导入
try:
    from scheduler.task_scheduler import TaskScheduler, DailyRotatingFileHandler
    from scheduler.config_manager import ConfigManager
except ImportError:
    # 如果作为包运行，尝试相对导入
    try:
        from task_scheduler import TaskScheduler, DailyRotatingFileHandler
        from config_manager import ConfigManager
    except ImportError as e:
        print(f"导入错误: {e}", file=sys.stderr)
        print("请确保项目结构正确", file=sys.stderr)
        sys.exit(1)


class CronPySchedulerService(win32serviceutil.ServiceFramework):
    """Python定时任务调度器Windows服务"""

    _svc_name_ = "CronPyScheduler"
    _svc_display_name_ = "Cron Python Scheduler"
    _svc_description_ = "Windows定时任务调度服务，支持cron表达式"

    def __init__(self, args):
        """初始化服务"""
        win32serviceutil.ServiceFramework.__init__(self, args)

        # 服务状态
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        self.is_alive = True

        # 调度器实例
        self.scheduler: Optional[TaskScheduler] = None
        self.config_manager: Optional[ConfigManager] = None

        # 设置工作目录到cronpy根目录
        self.working_dir = parent_dir  # parent_dir是从文件路径计算出的cronpy根目录
        os.chdir(self.working_dir)
        
        # 初始化日志配置
        self._setup_logging()

    def _setup_logging(self):
        """设置服务日志配置"""
        log_path = os.path.join(self.working_dir, 'scheduler.log')
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s [%(levelname)s] %(message)s',
            handlers=[
                DailyRotatingFileHandler(log_path, encoding='utf-8', backup_count=7),
                logging.StreamHandler(sys.stdout)  # 添加stdout处理器
            ],
            force=True
        )

    def SvcStop(self):
        """停止服务"""
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        self.is_alive = False

        # 停止调度器
        if self.scheduler:
            self.scheduler.stop()

        # 设置停止事件
        win32event.SetEvent(self.hWaitStop)

        self.ReportServiceStatus(win32service.SERVICE_STOPPED)

    def SvcDoRun(self):
        """运行服务"""
        log_path = None
        try:
            # 报告服务正在启动
            servicemanager.LogMsg(
                servicemanager.EVENTLOG_INFORMATION_TYPE,
                servicemanager.PYS_SERVICE_STARTED,
                (self._svc_name_, '')
            )

            self.ReportServiceStatus(win32service.SERVICE_START_PENDING)

            # 初始化配置管理器，使用正确的配置目录
            config_dir_path = os.path.join(self.working_dir, 'config')

            # 首先设置基本的日志（写入文件）
            log_path = os.path.join(self.working_dir, 'scheduler.log')
            os.makedirs(os.path.dirname(log_path), exist_ok=True)

            # 确保日志文件可以写入
            try:
                with open(log_path, 'a', encoding='utf-8') as f:
                    f.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} [INFO] 服务启动初始化\n")
            except Exception as log_error:
                # 如果无法写入日志文件，使用事件日志
                servicemanager.LogErrorMsg(f"无法创建日志文件: {str(log_error)}")
                raise log_error

            # 设置日志 - 只使用文件处理器，避免在服务中输出到控制台
            logging.basicConfig(
                level=logging.INFO,
                format='%(asctime)s [%(levelname)s] %(message)s',
                handlers=[
                    DailyRotatingFileHandler(log_path, encoding='utf-8', backup_count=7)
                ],
                force=True  # 强制重新配置日志
            )

            logging.info("=== CronPy调度器服务正在启动 ===")
            logging.info(f"工作目录: {self.working_dir}")
            logging.info(f"日志文件: {log_path}")
            logging.info(f"配置目录: {config_dir_path}")

            # 检查工作目录是否存在
            if not os.path.exists(self.working_dir):
                error_msg = f"工作目录不存在: {self.working_dir}"
                logging.error(error_msg)
                raise FileNotFoundError(error_msg)

            # 检查配置文件是否存在
            config_file = os.path.join(config_dir_path, 'scheduler.yaml')
            if not os.path.exists(config_file):
                error_msg = f"配置文件不存在: {config_file}"
                logging.error(error_msg)
                raise FileNotFoundError(error_msg)

            # 初始化配置管理器
            try:
                self.config_manager = ConfigManager(config_dir=config_dir_path)
                logging.info("配置管理器初始化成功")
            except Exception as config_error:
                logging.error(f"配置管理器初始化失败: {config_error}")
                raise config_error

            # 获取配置
            try:
                scheduler_config = self.config_manager.get_scheduler_config()
                log_file = scheduler_config.get("log_file", "scheduler.log")
                log_dir = scheduler_config.get("log_dir", "logs")  # 获取日志目录配置
                max_workers = scheduler_config.get("max_workers", 5)
                logging.info(f"配置加载成功: max_workers={max_workers}, log_file={log_file}, log_dir={log_dir}")
            except Exception as config_error:
                logging.error(f"配置加载失败: {config_error}")
                raise config_error

            # 创建调度器
            try:
                # 将日志目录配置传递给任务调度器
                self.scheduler = TaskScheduler(max_workers=max_workers, log_file=log_file, log_dir=log_dir)
                logging.info("调度器创建成功")
            except Exception as scheduler_error:
                logging.error(f"调度器创建失败: {scheduler_error}")
                raise scheduler_error

            # 加载任务
            try:
                tasks = self.config_manager.load_all_tasks()
                loaded_count = 0
                for task in tasks:
                    if task.enabled:
                        self.scheduler.add_task(task)
                        loaded_count += 1
                        logging.info(f"加载任务: {task.name} ({task.task_id})")
                logging.info(f"任务加载完成，共加载 {loaded_count} 个启用任务")
            except Exception as task_error:
                logging.error(f"任务加载失败: {task_error}")
                raise task_error

            # 启动调度器
            try:
                self.scheduler.start()
                logging.info("调度器启动成功")
            except Exception as start_error:
                logging.error(f"调度器启动失败: {start_error}")
                raise start_error

            # 报告服务已启动
            self.ReportServiceStatus(win32service.SERVICE_RUNNING)
            logging.info("=== CronPy调度器服务启动完成 ===")

            # 自动保存状态的间隔（秒）
            auto_save_interval = scheduler_config.get("auto_save_interval", 60)
            last_save_time = time.time()

            # 服务主循环
            while self.is_alive:
                # 检查是否需要保存状态
                current_time = time.time()
                if current_time - last_save_time >= auto_save_interval:
                    try:
                        state_file = os.path.join(self.working_dir, "config", "scheduler_state.json")
                        os.makedirs(os.path.dirname(state_file), exist_ok=True)
                        self.scheduler.save_state(state_file)
                        last_save_time = current_time
                        logging.debug("调度器状态已自动保存")
                    except Exception as e:
                        logging.error(f"自动保存状态失败: {e}")

                # 等待停止事件或超时
                result = win32event.WaitForSingleObject(self.hWaitStop, 1000)  # 1秒超时

                if result == win32event.WAIT_OBJECT_0:
                    # 收到停止信号
                    break

            logging.info("=== CronPy调度器服务正在停止 ===")

            # 保存最终状态
            try:
                state_file = os.path.join(self.working_dir, "config", "scheduler_state.json")
                os.makedirs(os.path.dirname(state_file), exist_ok=True)
                self.scheduler.save_state(state_file)
                logging.info("最终状态已保存")
            except Exception as e:
                logging.error(f"保存最终状态失败: {e}")

            # 停止调度器
            if self.scheduler:
                self.scheduler.stop()
                logging.info("调度器已停止")

            logging.info("=== CronPy调度器服务已停止 ===")

        except Exception as e:
            error_msg = f"服务运行出错: {str(e)}"
            print(f"ERROR: {error_msg}")  # 调试输出

            # 尝试记录到日志文件
            if log_path and os.path.exists(os.path.dirname(log_path)):
                try:
                    with open(log_path, 'a', encoding='utf-8') as f:
                        f.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} [ERROR] {error_msg}\n")
                        import traceback
                        f.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} [ERROR] 详细错误信息:\n")
                        f.write(traceback.format_exc())
                        f.write("\n")
                except:
                    pass

            # 记录到Windows事件日志
            try:
                servicemanager.LogErrorMsg(error_msg)
            except:
                pass

            self.ReportServiceStatus(win32service.SERVICE_STOPPED)
            raise  # 重新抛出异常，确保服务正确停止

    def ReportServiceStatus(self, status):
        """报告服务状态"""
        win32serviceutil.ServiceFramework.ReportServiceStatus(self, status)


class ServiceManager:
    """服务管理器"""

    def __init__(self):
        """初始化服务管理器"""
        self.service_class = CronPySchedulerService

    def install_service(self):
        """安装服务"""
        try:
            # 获取当前Python解释器路径
            python_exe = sys.executable
            script_path = os.path.abspath(__file__)

            # 构建服务命令行
            service_cmd = f'"{python_exe}" "{script_path}"'

            print("正在安装CronPy调度器服务...")
            print(f"服务名称: {CronPySchedulerService._svc_name_}")
            print(f"显示名称: {CronPySchedulerService._svc_display_name_}")
            print(f"服务命令: {service_cmd}")

            # 使用pywin32安装服务
            # 注意：第一个参数应该是服务类字符串名，而不是类对象
            win32serviceutil.InstallService(
                None,  # 使用默认服务类
                CronPySchedulerService._svc_name_,
                CronPySchedulerService._svc_display_name_,
                startType=win32service.SERVICE_AUTO_START,
                exeName=service_cmd,
                description=CronPySchedulerService._svc_description_
            )

            print("服务安装成功！")
            print("您可以使用以下命令管理服务：")
            print("  启动服务: net start CronPyScheduler")
            print("  停止服务: net stop CronPyScheduler")
            print("  卸载服务: sc delete CronPyScheduler")
            print("  或使用: python windows_service.py start/stop/status")

        except Exception as e:
            print(f"安装服务失败: {e}")
            print("请确保以管理员身份运行此命令")

    def uninstall_service(self):
        """卸载服务"""
        try:
            print("正在卸载CronPy调度器服务...")
            win32serviceutil.RemoveService(CronPySchedulerService._svc_name_)
            print("服务卸载成功！")
        except Exception as e:
            print(f"卸载服务失败: {e}")

    def start_service(self):
        """启动服务"""
        try:
            print("正在启动CronPy调度器服务...")
            win32serviceutil.StartService(CronPySchedulerService._svc_name_)
            print("服务启动成功！")

            # 等待一会儿检查服务是否真的启动了
            time.sleep(2)
            status = self.get_service_status()
            if status == win32service.SERVICE_RUNNING:
                print("✓ 服务已成功运行")
            else:
                print("⚠ 服务启动可能有问题，请检查服务状态")

        except Exception as e:
            print(f"启动服务失败: {e}")
            print("可能的原因:")
            print("1. 服务未正确安装")
            print("2. 缺少管理员权限")
            print("3. 配置文件或依赖项问题")
            print("请尝试重新安装服务或以管理员身份运行")

    def stop_service(self):
        """停止服务"""
        try:
            print("正在停止CronPy调度器服务...")
            win32serviceutil.StopService(CronPySchedulerService._svc_name_)
            print("服务停止成功！")
        except Exception as e:
            print(f"停止服务失败: {e}")

    def restart_service(self):
        """重启服务"""
        try:
            print("正在重启CronPy调度器服务...")
            self.stop_service()
            time.sleep(2)  # 等待服务完全停止
            self.start_service()
            print("服务重启成功！")
        except Exception as e:
            print(f"重启服务失败: {e}")

    def get_service_status(self):
        """获取服务状态"""
        try:
            import win32service
            import win32con

            scm = win32service.OpenSCManager(None, None, win32con.GENERIC_READ)
            try:
                service = win32service.OpenService(scm, CronPySchedulerService._svc_name_,
                                                 win32con.GENERIC_READ)
                try:
                    status = win32service.QueryServiceStatus(service)
                    status_map = {
                        win32service.SERVICE_STOPPED: "已停止",
                        win32service.SERVICE_START_PENDING: "启动中",
                        win32service.SERVICE_STOP_PENDING: "停止中",
                        win32service.SERVICE_RUNNING: "运行中",
                        win32service.SERVICE_CONTINUE_PENDING: "继续中",
                        win32service.SERVICE_PAUSE_PENDING: "暂停中",
                        win32service.SERVICE_PAUSED: "已暂停"
                    }

                    status_text = status_map.get(status[1], f"未知状态({status[1]})")
                    print(f"服务状态: {status_text}")

                    return status[1]

                finally:
                    win32service.CloseServiceHandle(service)
            finally:
                win32service.CloseServiceHandle(scm)

        except Exception as e:
            print(f"获取服务状态失败: {e}")
            return None


def main():
    """主函数"""
    if len(sys.argv) == 1:
        # 作为服务运行
        servicemanager.Initialize()
        servicemanager.PrepareToHostSingle(CronPySchedulerService)
        servicemanager.StartServiceCtrlDispatcher()
    else:
        # 作为命令行工具运行
        service_manager = ServiceManager()

        command = sys.argv[1].lower()

        if command == "install":
            service_manager.install_service()
        elif command == "uninstall" or command == "remove":
            service_manager.uninstall_service()
        elif command == "start":
            service_manager.start_service()
        elif command == "stop":
            service_manager.stop_service()
        elif command == "restart":
            service_manager.restart_service()
        elif command == "status":
            service_manager.get_service_status()
        else:
            print("CronPy调度器服务管理工具")
            print("用法:")
            print("  python windows_service.py install    - 安装服务")
            print("  python windows_service.py uninstall  - 卸载服务")
            print("  python windows_service.py start      - 启动服务")
            print("  python windows_service.py stop       - 停止服务")
            print("  python windows_service.py restart    - 重启服务")
            print("  python windows_service.py status     - 查看服务状态")
            print()
            print("或者直接运行 (无参数) 以启动服务进行调试")


if __name__ == "__main__":
    main()
